package top.codedance.iotp.loadblance;

import com.alibaba.fastjson.JSONObject;
import top.codedance.iotp.common.enums.HttpStatus;
import top.codedance.iotp.common.util.HttpRequestUtil;
import top.codedance.iotp.loadblance.algorithm.Adapter;
import top.codedance.iotp.loadblance.common.AlgorithmType;
import top.codedance.iotp.loadblance.entiy.BlanceServerInfo;
import top.codedance.iotp.loadblance.entiy.RegistyUri;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class IotServerLoadBlance {

    private static IotServerLoadBlance self = null;
    private static Map<String, BlanceServerInfo> blanceCache = new HashMap<>();
    private static final String HTTP_CACHE_KEY_PREFIX = "HTTP_";
    private static final String WS_CACHE_KEY_PREFIX = "WS_";

    protected List<RegistyUri> registryUrls;
    protected AlgorithmType at = AlgorithmType.AUTO;

    protected IotServerLoadBlance(List<RegistyUri> registryUrls) {
        this.registryUrls = registryUrls;
    }

    protected IotServerLoadBlance(List<RegistyUri> registryUrls, AlgorithmType at) {
        this.registryUrls = registryUrls;
        this.at = at;
    }

    public static IotServerLoadBlance getInstance(List<RegistyUri> registryUrls, AlgorithmType at){
        if(self == null){
            synchronized (IotServerLoadBlance.class){
                if(self == null){
                    self = new IotServerLoadBlance(registryUrls, at);
                }
            }
        }
        return self;
    }

    public static IotServerLoadBlance getInstance(List<RegistyUri> registryUrl){
        return getInstance(registryUrl, AlgorithmType.CYCLE);
    }

    /**
     * 负载均衡
     * @param deviceId
     * @return
     */
    public String blanceHttp(String deviceId){
        String cacheKey = HTTP_CACHE_KEY_PREFIX + deviceId;
        if(blanceCache.containsKey(cacheKey)){
            BlanceServerInfo blanceServerInfo = blanceCache.get(cacheKey);
            if(System.currentTimeMillis() - blanceServerInfo.getTimestamp() < 1000 * 60 * 10){
                return blanceServerInfo.getServer();
            }
        }

        try {
            Adapter adapter = at.getAlgorithm().newInstance();
            return adapter.execute(registryUrls, (registryUrl) -> {
                String result = null;
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("deviceId", deviceId);
                JSONObject body = HttpRequestUtil.post(registryUrl + "/rest/api/serverMeta", jsonObject.toJSONString());
                if (body != null && body.getIntValue("status") == HttpStatus.OK.getCode()) {
                    JSONObject data = body.getJSONObject("data");
                    String s = data.getString("httpBindHost");
                    if(s.isEmpty()){
                        result = "http://" + data.getString("bindIp") + ":" + data.getString("httpPort");
                    }else{
                        result = s;
                    }
                    blanceCache.put(cacheKey, new BlanceServerInfo(result, System.currentTimeMillis()));
                }else{
                    blanceCache.remove(cacheKey);
                }
                return result;
            });
        }catch (Exception ex){
            blanceCache.remove(cacheKey);
            return null;
        }
    }

    /**
     * 负载均衡
     * @param deviceId
     * @return
     */
    public String blanceWs(String deviceId){
        String cacheKey = WS_CACHE_KEY_PREFIX + deviceId;
        if(blanceCache.containsKey(cacheKey)){
            BlanceServerInfo blanceServerInfo = blanceCache.get(cacheKey);
            if(System.currentTimeMillis() - blanceServerInfo.getTimestamp() < 1000 * 60 * 10){
                return blanceServerInfo.getServer();
            }
        }

        try {
            Adapter adapter = at.getAlgorithm().newInstance();
            return adapter.execute(registryUrls, (registryUrl) -> {
                String result = null;
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("deviceId", deviceId);
                JSONObject body = HttpRequestUtil.post(registryUrl + "/rest/api/serverMeta", jsonObject.toJSONString());
                if (body != null && body.getIntValue("status") == HttpStatus.OK.getCode()) {
                    JSONObject d = body.getJSONObject("data");
                    String s = d.getString("wsBindHost");
                    if(s.isEmpty()){
                        result = "ws://" + d.getString("bindIp") + ":" + d.getString("wsPort");
                    }else{
                        result = s;
                    }
                    blanceCache.put(cacheKey, new BlanceServerInfo(result, System.currentTimeMillis()));
                }else{
                    blanceCache.remove(cacheKey);
                }
                return result;
            });
        }catch (Exception ex){
            blanceCache.remove(cacheKey);
            return null;
        }
    }

    /**
     * 在http缓存中请除deviceId设备的缓存
     * @param deviceId
     */
    public void clearHttpCache(String deviceId){
        String cacheKey = HTTP_CACHE_KEY_PREFIX + deviceId;
        if(blanceCache.containsKey(cacheKey))
            blanceCache.remove(cacheKey);
    }

    /**
     * 重新HTTP负载均衡
     * @param deviceId
     * @return
     */
    public String reBlanceHttp(String deviceId) {
        clearHttpCache(deviceId);
        return blanceHttp(deviceId);
    }

    /**
     * 在ws缓存中请除deviceId设备的缓存
     * @param deviceId
     */
    public void clearWsCache(String deviceId){
        String cacheKey = WS_CACHE_KEY_PREFIX + deviceId;
        if(blanceCache.containsKey(cacheKey))
            blanceCache.remove(cacheKey);
    }

    /**
     * 重新WS负载均衡
     * @param deviceId
     * @return
     */
    public String reBlanceWs(String deviceId){
        clearWsCache(deviceId);
        return blanceWs(deviceId);
    }
}